import Geo from './Geo.js';
import Mat from './Mat.js';
import { Matrix4, Vector3 } from '../three.module.js';

const defAttr = () => ({
    mat: null,
    geo: null,

    transformM4: new Matrix4(),// 全部变换矩阵
    tranM4NeedUpdate: false,
})

export default class Obj3D {
    constructor(attr) {
        Object.assign(this, defAttr(), attr);

        this.parent = null;
        this.children = [];
    }

    init(gl) {
        const { mat, geo } = this;
        geo && geo.init(gl);
        mat && mat.init(gl);
    }

    setUniform(name, uniform) {
        const { mat, children } = this;
        mat && mat.setData(name, uniform);

        for (let obj of children) {
            obj.setUniform(name, uniform);
        }
    }

    setAttrib(name, attrib) {
        const { geo, children } = this;
        geo && geo.setData(name, attrib);

        for (let obj of children) {
            obj.setAttrib(name, attrib);
        }
    }

    update(gl, attribs, uniforms) {
        const { mat, geo, children } = this;
        mat && mat.update(gl, uniforms);
        geo && geo.update(gl, attribs);

        // for (let obj of children) {
        //     obj.mat.update(gl, uniforms);
        //     obj.geo.update(gl, attribs);
        // }
    }

    draw(gl) {
        const { children, mat, geo } = this;
        const { mode } = mat;
        const { drawType, count } = geo;

        if (!(mat == null || geo == null)) {
            for (let type of mode) {
                this[`_${drawType}`](gl, count, type);
            }
        }


        //渲染子类
        // for (let obj of children) {
        //     const geoC = obj.geo;
        //     const matC = obj.mat;
        //     for (let type of matC.mode) {
        //         obj[`_${geoC.drawType}`](gl, geoC.count, type);
        //     }
        // }
    }

    add(gl, ...child) {
        const { children } = this;
        for (let obj of child) {
            children.push(obj);
            obj.init(gl);
        }
    }

    setProgram(program) {
        this.mat.program = program;
    }

    _drawArrays(gl, count, mode) {
        gl.drawArrays(gl[mode], 0, count);
    }

    _drawElements(gl, count, mode) {
        gl.drawElements(gl[mode], count, gl.UNSIGNED_BYTE, 0)
    }

    allTransform() {
        const { mat, geo } = this;
        const { array, size } = geo.data['a_Position'];
        const { value } = mat.data['u_ModelM4'];

        let mm4 = new Matrix4();
        mm4.elements = value;

        let newArray = [];
        for (let i = 0; i < array.length; i += size) {
            let cur = null;
            if (size == 2) {
                cur = new Vector3(array[i], array[i + 1], 0);
            } else {
                cur = new Vector3(array[i], array[i + 1], array[i + 2]);
            }
            cur = cur.clone().applyMatrix4(mm4);
            if (size == 2) {
                newArray.push(cur.x, cur.y);
            } else {
                newArray.push(cur.x, cur.y, cur.z);
            }
        }
        // console.log(array)
        // console.log(newArray)
        //清空模型矩阵
        mat.setData('u_ModelM4', { value: new Matrix4().elements });
        // mat.data['u_ModelM4'].value = new Matrix4().elements;
        // mat.data['u_ModelM4'].needUpdate = false;
        //覆盖posiArray
        geo.setData('a_Position', { array: new Float32Array(newArray) });

    }







    clone() {
        return new Obj3D({
            mat: this.mat.clone(),
            geo: this.geo.clone(),
        });
    }

    default(data) {
        const curData = {};
        Object.assign(curData, obj3DAttr(), data);
        this.geo = new Geo({
            data: {
                'a_Position': {
                    size: curData.size, array: new Float32Array(curData.array)
                },
            },
        });
        if (curData.element) {
            this.geo.setElement(new Uint8Array(curData.element));
        }
        this.mat = new Mat({
            program: curData.program,
            mode: curData.mode,
            data: {
                'u_PvM4': { type: 'uniformMatrix4fv', value: curData.pvM4.elements },
                'u_ModelM4': { type: 'uniformMatrix4fv', value: curData.modelM4.elements },
                'u_PointSize': { type: 'uniform1f', value: curData.PointSize },
                'u_Color': { type: 'uniform4fv', value: curData.color, cRend: true },
            },
        });
        return this;
    }

    createAxis(program) {
        let create = function (array, color, element) {
            return new Obj3D({
                geo: new Geo({
                    data: {
                        'a_Position': {
                            size: 3, array: new Float32Array(array)
                        },
                    },
                    element: {
                        array: new Uint8Array(element)
                    }
                }),
                mat: new Mat({
                    program: program,
                    mode: ['LINES'],
                    data: {
                        'u_PvM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                        'u_ModelM4': { type: 'uniformMatrix4fv', value: new Matrix4().elements },
                        'u_Color': { type: 'uniform4fv', value: color },
                    }
                })
            });
        }
        let axis = create([
            10, 0, 0,
            -10, 0, 0,
            0, 10, 0,
            0, -10, 0,
            0, 0, 10,
            0, 0, -10,
        ], [1, 1, 1, 1], [0, 1, 2, 3, 4, 5]);
        let x = create([
            0, 0, 0,
            1, 0, 0
        ], [1, 0, 0, 1], [0, 1]);
        let y = create([
            0, 0, 0,
            0, 1, 0
        ], [0, 1, 0, 1], [0, 1]);
        let z = create([
            0, 0, 0,
            0, 0, 1
        ], [0, 0, 1, 1], [0, 1]);
        let arr = [axis, x, y, z];
        return arr;
    }


}

const obj3DAttr = () => ({
    program: null,
    array: [],
    element: null,
    pvM4: new Matrix4(),
    modelM4: new Matrix4(),
    color: [1, 1, 1, 1],
    PointSize: 30,
    size: 3,
    mode: ["POINTS"]
})